﻿#include "MainWindow.h"
#include "../utils/trim.h"
#include "CtrlWidget.h"
#include "OCCWidget.h"

#include "ui_MainWindow.h"

// 防止 Qt slots 宏与 pybind11 变量名冲突
#ifdef slots
#undef slots
#endif

#include <pybind11/embed.h>

// 启用字面量
using namespace pybind11::literals;

// 取消宏定义
#undef foreach
#undef near

#include <learn/alg/alg_covering.h>
#include <learn/alg/alg_distance.h>
#include <learn/alg/alg_energy.h>
#include <learn/alg/alg_sampler.h>
#include <learn/utils/timer.h>

using namespace xi;
using namespace xi::utils;

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    this->setWindowTitle("MainWindow");

    // 逐层级启用鼠标追踪
    this->setMouseTracking(true);
    ui->centralWidget->setMouseTracking(true);

    // 初始化连接
    QObject::connect(ui->actionObject, SIGNAL(triggered(bool)), this, SLOT(openFile()));
    QObject::connect(ui->actionCloud, SIGNAL(triggered(bool)), this, SLOT(openCloud()));
    QObject::connect(ui->actionDir, SIGNAL(triggered(bool)), this, SLOT(openDirectory()));
    QObject::connect(ui->actionSaveObj, SIGNAL(triggered(bool)), this, SLOT(saveFile()));
    QObject::connect(ui->actionSaveCloud, SIGNAL(triggered(bool)), this, SLOT(saveCloud()));
    QObject::connect(ui->actionDelete, SIGNAL(triggered(bool)), this, SLOT(deleteFile()));

    QObject::connect(ui->actionFPIA, SIGNAL(triggered(bool)), this, SLOT(coverFPIA()));
    QObject::connect(ui->actionEnergy, SIGNAL(triggered(bool)), this, SLOT(coverEnergy()));
    QObject::connect(ui->actionCoons, SIGNAL(triggered(bool)), this, SLOT(coverCoons()));
    QObject::connect(ui->actionDBS, SIGNAL(triggered(bool)), this, SLOT(coverDBS()));

    QObject::connect(ui->actionComputeD, SIGNAL(triggered(bool)), this, SLOT(computeDistance()));
    QObject::connect(ui->actionComputeE, SIGNAL(triggered(bool)), this, SLOT(computeEnergy()));
    QObject::connect(ui->actionWeight, SIGNAL(triggered(bool)), this, SLOT(rangeWeight()));

    QObject::connect(ui->actionDiscrete, SIGNAL(triggered(void)), this, SLOT(discrete()));

    // 构造完成后调用
    QTimer::singleShot(0, this, &MainWindow::initializeChildren);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::add_point_cloud(const std::vector<gp_Pnt> &points)
{
    if (points.empty())
        return;

    Handle(TColgp_HArray1OfPnt) cloud = new TColgp_HArray1OfPnt(1, points.size());
    for (int i = 1; i <= cloud->Size(); i++)
        cloud->SetValue(i, points[i - 1]);

    Handle(AIS_PointCloud) c(new AIS_PointCloud);
    c->SetPoints(cloud);
    m_clouds.push_back(c);
}

void MainWindow::add_curve(Handle(Geom_Curve) curve, double first, double last)
{
    TopoDS_Shape edge = BRepBuilderAPI_MakeEdge(curve, first, last);
    this->add_shape(edge);
}

void MainWindow::add_surface(Handle(Geom_Surface) surface)
{
    TopoDS_Shape face = BRepBuilderAPI_MakeFace(surface, 1e-8);
    this->add_shape(face);
}

void MainWindow::add_shape(const TopoDS_Shape &shape)
{
    m_shapes.push_back(shape);
}

void MainWindow::add_triangulation(Handle(Poly_Triangulation) triangulation)
{
    m_triangulations.push_back(triangulation);
}

void MainWindow::read_stp(const char *path)
{
    // 创建读取器
    STEPControl_Reader reader;
    reader.ReadFile(Standard_CString(path));

    // 检查读取信息
    reader.PrintCheckLoad(Standard_True, IFSelect_ItemsByEntity);

    // 获得可翻译的根实体数量，然后翻译所有实体，获得成功的数量
    Standard_Integer NbRoots = reader.NbRootsForTransfer();
    Standard_Integer NbTrans = reader.TransferRoots();

    // 检查翻译信息
    reader.PrintCheckTransfer(Standard_True, IFSelect_ItemsByEntity);

    // 添加获得的形状
    for (int i = 1; i <= reader.NbShapes(); i++)
    {
        auto shape = reader.Shape(i);
        this->add_shape(shape);
    }

    std::cout << "Read " << reader.NbShapes() << " shapes." << std::endl;
}

void MainWindow::write_stp(const char *path)
{
    // 创建写入器
    STEPControl_Writer writer;

    // 翻译所有形状
    for (auto sh : m_shapes)
        writer.Transfer(sh, STEPControl_AsIs);

    // 写入文件
    writer.Write(Standard_CString(path));
}

void MainWindow::read_igs(const char *path)
{
    // 读取器
    IGESControl_Reader reader;

    // 读取IGES文件
    Standard_Integer status = reader.ReadFile(path);

    Standard_Integer NbRoots = reader.NbRootsForTransfer();
    Standard_Integer NbTrans = reader.TransferRoots();

    // 添加获得的形状
    for (int i = 1; i <= reader.NbShapes(); i++)
    {
        auto shape = reader.Shape(i);
        this->add_shape(shape);
    }
}

void MainWindow::write_igs(const char *path)
{
    // 创建写入器
    IGESControl_Writer writer;

    // 翻译所有形状
    for (auto sh : m_shapes)
        writer.AddShape(sh);

    // 写入文件
    writer.Write(Standard_CString(path));
}

void MainWindow::read_brep(const char *path)
{
    // 创建空形状保存
    TopoDS_Shape shape;

    // 构建器
    BRep_Builder builder;
    if (BRepTools::Read(shape, path, builder))
        this->add_shape(shape);
    else
        QMessageBox::information(this, "Tip", "Failed.");
}

void MainWindow::write_brep(const char *path)
{
}

void MainWindow::read_cloud(const char *path)
{
    // 创建空形状保存
    TopoDS_Shape shape;

    // 构建器
    BRep_Builder builder;
    if (BRepTools::Read(shape, path, builder))
    {
        if (shape.ShapeType() == TopAbs_COMPOUND)
        {
            TopExp_Explorer Ex;
            std::vector<gp_Pnt> points;
            for (Ex.Init(shape, TopAbs_VERTEX); Ex.More(); Ex.Next())
            {
                TopoDS_Vertex V;
                V = TopoDS::Vertex(Ex.Current());

                gp_Pnt P = BRep_Tool::Pnt(V);
                points.push_back(P);
            }

            if (!points.empty())
                this->add_point_cloud(points);
        }
        else
            this->add_shape(shape);
    }
    else
        QMessageBox::information(this, "Tip", "Failed.");
}

void MainWindow::write_cloud(const char *path)
{
    // 创建复合体
    TopoDS_Compound compound;
    BRep_Builder builder;
    builder.MakeCompound(compound);

    auto cloud = ui->occWidget->selected_point_cloud();
    if (!cloud.IsNull())
    {
        auto points = cloud->GetPoints();

        // 将每个点转换为一个顶点并添加到复合体中
        for (int i = 1; i <= points->VertexNumber(); ++i)
        {
            Standard_Real x, y, z;
            points->Vertice(i, x, y, z);
            TopoDS_Vertex vertex = BRepBuilderAPI_MakeVertex({x, y, z});
            builder.Add(compound, vertex);
        }

        // 将复合体导出为 BREP 文件
        BRepTools::Write(compound, path);
    }
}

void MainWindow::read_obj(const char *path)
{
    xi::utils::ObjTool reader;
    reader.read(path);

    std::vector<gp_Pnt> points;
    for (auto &p : reader.points)
        points.push_back(Adaptor_OCCVec(p));
    add_point_cloud(points);

    for (auto &curve : reader.splineCurves)
    {
        Adaptor_OCCBSplineCurve<3> adaptor(curve.get());
        Handle(Geom_BSplineCurve) c = adaptor;
        add_curve(c, c->FirstParameter(), c->LastParameter());
    }

    for (auto &curve : reader.splineCurvesR)
    {
        Adaptor_OCCBSplineCurve<3, true> adaptor(curve.get());
        Handle(Geom_BSplineCurve) c = adaptor;
        add_curve(c, c->FirstParameter(), c->LastParameter());
    }

    for (auto &surface : reader.splineSurfaces)
    {
        Adaptor_OCCBSplineSurface<3> adaptor(surface.get());
        Handle(Geom_BSplineSurface) s = adaptor;
        add_surface(s);
    }

    for (auto &surface : reader.splineSurfacesR)
    {
        Adaptor_OCCBSplineSurface<3, true> adaptor(surface.get());
        Handle(Geom_BSplineSurface) s = adaptor;
        add_surface(s);
    }
}

void MainWindow::write_obj(const char *path)
{
}

OCCWidget *MainWindow::occ_widget()
{
    return static_cast<OCCWidget *>(ui->occWidget);
}

CtrlWidget *MainWindow::ctrl_widget()
{
    return static_cast<CtrlWidget *>(ui->ctrlWidget);
}

void MainWindow::show_message(std::string msg)
{
    ui->statusBar->showMessage(msg.c_str(), 10000);
}

void MainWindow::resizeEvent(QResizeEvent *event)
{
    auto s = event->size();
    ui->ctrlWidget->resize(450, s.height());
    ui->ctrlWidget->move(s.width() - 450, 0);
    ui->occWidget->resize(s.width() - 450, s.height());
}

void MainWindow::initializeChildren()
{
    ui->occWidget->initialize();
    ui->ctrlWidget->initialize();
}

void MainWindow::openFile()
{
    static QString directoryPath = TEST_DATA_PATH;
    QString filename = QFileDialog::getOpenFileName(this, "Choose Model", directoryPath,
                                                    "All Files(*.*);;STEP (*.stp);;BRep (*.brep);;IGS (*.igs)");
    if (filename != "")
    {
        QFileInfo fileInfo(filename);
        directoryPath = fileInfo.absolutePath();

        if (filename.endsWith(".stp") || filename.endsWith(".step"))
            read_stp(filename.toStdString().c_str());
        else if (filename.endsWith(".brep"))
            read_brep(filename.toStdString().c_str());
        else if (filename.endsWith(".igs"))
            read_igs(filename.toStdString().c_str());
        else if (filename.endsWith(".obj"))
            read_obj(filename.toStdString().c_str());

        // 刷新交互
        ui->occWidget->update_interactive_context();
    }

    // 存在物体则适应物体
    ui->occWidget->fit_all_shapes();
}

void MainWindow::openCloud()
{
    static QString directoryPath = TEST_DATA_PATH;
    QString filename = QFileDialog::getOpenFileName(this, "Choose Model", directoryPath, "BRep (*.brep)");
    if (filename != "")
    {
        QFileInfo fileInfo(filename);
        directoryPath = fileInfo.absolutePath();

        if (filename.endsWith(".brep"))
            read_cloud(filename.toStdString().c_str());

        // 刷新交互
        ui->occWidget->update_interactive_context();
    }

    // 存在物体则适应物体
    ui->occWidget->fit_all_shapes();
}

void MainWindow::openDirectory()
{
    static QString directoryPath = TEST_DATA_PATH;
    QString dirname = QFileDialog::getExistingDirectory(this, "Choose Directory", directoryPath,
                                                        QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
    if (dirname != "")
    {
        QDir dir(dirname);

        // 获取目录下的所有文件名（不包括子目录中的文件）
        QStringList fileList = dir.entryList(QDir::Files);
        for (const QString &filename : fileList)
        {
            if (filename.endsWith(".brep"))
                read_brep((dirname + "/" + filename).toStdString().c_str());
            else if (filename.endsWith(".stp") || filename.endsWith(".step"))
                read_stp((dirname + "/" + filename).toStdString().c_str());
            else if (filename.endsWith(".igs"))
                read_igs((dirname + "/" + filename).toStdString().c_str());
            else if (filename.endsWith(".obj"))
                read_obj((dirname + "/" + filename).toStdString().c_str());
        }

        // 刷新交互
        ui->occWidget->update_interactive_context();
    }

    // 存在物体则适应物体
    ui->occWidget->fit_all_shapes();
}

void MainWindow::saveFile()
{
    static QString directoryPath = TEST_DATA_PATH;
    QString filename = QFileDialog::getSaveFileName(this, "Save File", directoryPath,
                                                    "STEP (*.stp);;BRep (*.brep);;IGS (*.igs);;OBJ (*.obj)");
    if (filename != "")
    {
        QFileInfo fileInfo(filename);
        directoryPath = fileInfo.absolutePath();

        // 获取路径，添加后缀
        std::string name = filename.toStdString();

        if (filename.endsWith(".stp") || filename.endsWith(".step"))
            write_stp(filename.toStdString().c_str());
        else if (filename.endsWith(".brep"))
            write_brep(filename.toStdString().c_str());
        else if (filename.endsWith(".igs"))
            write_igs(filename.toStdString().c_str());
        else if (filename.endsWith(".obj"))
            write_obj(filename.toStdString().c_str());

        QMessageBox::information(this, "Tip", "Saved.");
    }
}

void MainWindow::saveCloud()
{
    static QString directoryPath = TEST_DATA_PATH;
    QString filename = QFileDialog::getSaveFileName(this, "Save File", directoryPath, "BRep (*.brep)");
    if (filename != "")
    {
        QFileInfo fileInfo(filename);
        directoryPath = fileInfo.absolutePath();

        // 获取路径，添加后缀
        std::string name = filename.toStdString();
        if (filename.endsWith(".brep"))
            write_cloud(filename.toStdString().c_str());

        QMessageBox::information(this, "Tip", "Saved.");
    }
}

void MainWindow::deleteFile()
{
    m_shapes.clear();
    m_selected.clear();
    m_clouds.clear();
    m_triangulations.clear();

    // 刷新窗口
    ui->occWidget->update_interactive_context();
    ui->ctrlWidget->update();
}

void MainWindow::coverFPIA()
{
    std::vector<TopoDS_Shape> faces;
    auto shapes = m_selected;
    int edgeCount = 0;
    std::vector<std::shared_ptr<xi::geom::Geom_BSplineCurve<3>>> curves;
    for (auto sh : shapes)
    {
        TopoDS_Edge V;
        BRep_Tool tool;
        V = TopoDS::Edge(sh);

        double first, last;
        auto curve = Handle(Geom_BSplineCurve)::DownCast(tool.Curve(V, first, last));
        curves.push_back(Adaptor_OCCBSplineCurve<3>(curve));
        edgeCount++;
    }

    auto boundaries = curves;
    auto covering = xi::alg::Alg_FairingPIA_Covering{};

    TIMER_START(Covering);
    auto S = covering.cover(std::move(boundaries));
    TIMER_END(Covering);

    std::cout << "distance: " << xi::alg::Alg_Energy::surface_least_square_energy(covering.data, covering.params, S)
              << std::endl;
    auto [E1, E2] = xi::alg::Alg_Energy::surface_stretch_bending_energy(S);
    std::cout << "e1: " << E1 << " e2: " << E2 << std::endl;

    auto face = generate_trimmed_face(boundaries, S);
    faces.push_back(face);

    m_shapes.clear();
    for (auto face : faces)
        add_shape(face);

    ui->occWidget->update_interactive_context();

    return;

    //////////////////////////////////////////////////////////////////////////////////

    for (auto sh : m_shapes)
    {
        TopExp_Explorer Ex;
        BRep_Tool tool;

        int edgeCount = 0;
        std::vector<std::shared_ptr<xi::geom::Geom_BSplineCurve<3>>> curves;
        for (Ex.Init(sh, TopAbs_EDGE); Ex.More(); Ex.Next())
        {
            TopoDS_Edge V;
            V = TopoDS::Edge(Ex.Current());

            double first, last;
            auto curve = Handle(Geom_BSplineCurve)::DownCast(tool.Curve(V, first, last));
            curves.push_back(Adaptor_OCCBSplineCurve<3>(curve));
            edgeCount++;
        }

        auto boundaries = curves;
        auto covering = xi::alg::Alg_FairingPIA_Covering{};

        TIMER_START(Covering);
        auto S = covering.cover(std::move(boundaries));
        TIMER_END(Covering);

        std::cout << "distance: " << xi::alg::Alg_Energy::surface_least_square_energy(covering.data, covering.params, S)
                  << std::endl;
        auto [E1, E2] = xi::alg::Alg_Energy::surface_stretch_bending_energy(S);
        std::cout << "e1: " << E1 << " e2: " << E2 << std::endl;

        auto face = generate_trimmed_face(boundaries, S);
        faces.push_back(face);
    }

    m_shapes.clear();
    for (auto face : faces)
        add_shape(face);

    ui->occWidget->update_interactive_context();
}

void MainWindow::coverEnergy()
{
    std::vector<TopoDS_Shape> faces;
    auto shapes = m_selected;
    int edgeCount = 0;
    std::vector<std::shared_ptr<xi::geom::Geom_BSplineCurve<3>>> curves;
    for (auto sh : shapes)
    {
        TopoDS_Edge V;
        BRep_Tool tool;
        V = TopoDS::Edge(sh);

        double first, last;
        auto curve = Handle(Geom_BSplineCurve)::DownCast(tool.Curve(V, first, last));
        curves.push_back(Adaptor_OCCBSplineCurve<3>(curve));
        edgeCount++;
    }

    auto boundaries = curves;
    auto covering = xi::alg::Alg_FairingEnergy_Covering{};
    // covering.uPoleNum = 36;
    // covering.vPoleNum = 36;
    // covering.wBending = 1e-8;
    // covering.wStretch = 1e-8;

    TIMER_START(Covering);
    auto S = covering.cover(std::move(boundaries));
    TIMER_END(Covering);

    std::cout << "distance: " << xi::alg::Alg_Energy::surface_least_square_energy(covering.data, covering.params, S)
              << std::endl;
    auto [E1, E2] = xi::alg::Alg_Energy::surface_stretch_bending_energy(S);
    std::cout << "e1: " << E1 << " e2: " << E2 << std::endl;

    auto face = generate_trimmed_face(boundaries, S);
    faces.push_back(face);

    m_shapes.clear();
    for (auto face : faces)
        add_shape(face);

    ui->occWidget->update_interactive_context();

    return;

    ////////////////////////////////////////////////////////////////////////////

    for (auto sh : m_shapes)
    {
        TopExp_Explorer Ex;
        BRep_Tool tool;

        int edgeCount = 0;
        std::vector<std::shared_ptr<xi::geom::Geom_BSplineCurve<3>>> curves;
        for (Ex.Init(sh, TopAbs_EDGE); Ex.More(); Ex.Next())
        {
            TopoDS_Edge V;
            V = TopoDS::Edge(Ex.Current());

            double first, last;
            auto curve = Handle(Geom_BSplineCurve)::DownCast(tool.Curve(V, first, last));
            curves.push_back(Adaptor_OCCBSplineCurve<3>(curve));
            edgeCount++;
        }

        auto boundaries = curves;
        auto covering = xi::alg::Alg_FairingEnergy_Covering{};
        // covering.uPoleNum = 36;
        // covering.vPoleNum = 36;
        // covering.wBending = 1e-8;
        // covering.wStretch = 1e-8;

        TIMER_START(Covering);
        auto S = covering.cover(std::move(boundaries));
        TIMER_END(Covering);

        std::cout << "distance: " << xi::alg::Alg_Energy::surface_least_square_energy(covering.data, covering.params, S)
                  << std::endl;
        auto [E1, E2] = xi::alg::Alg_Energy::surface_stretch_bending_energy(S);
        std::cout << "e1: " << E1 << " e2: " << E2 << std::endl;

        auto face = generate_trimmed_face(boundaries, S);
        faces.push_back(face);
    }

    m_shapes.clear();
    for (auto face : faces)
        add_shape(face);

    ui->occWidget->update_interactive_context();
}

void MainWindow::coverCoons()
{
    std::vector<TopoDS_Shape> faces;
    for (auto sh : m_shapes)
    {
        TopExp_Explorer Ex;
        BRep_Tool tool;

        int edgeCount = 0;
        std::vector<std::shared_ptr<xi::geom::Geom_BSplineCurve<3>>> curves;
        for (Ex.Init(sh, TopAbs_EDGE); Ex.More(); Ex.Next())
        {
            TopoDS_Edge V;
            V = TopoDS::Edge(Ex.Current());

            double first, last;
            auto curve = Handle(Geom_BSplineCurve)::DownCast(tool.Curve(V, first, last));
            curves.push_back(Adaptor_OCCBSplineCurve<3>(curve));
            edgeCount++;
        }

        auto boundaries = curves;
        auto covering = xi::alg::Alg_Coons_Covering{};

        TIMER_START(Covering);
        auto S = covering.cover(std::move(boundaries));
        TIMER_END(Covering);

        std::cout << "distance: " << 0 << std::endl;
        auto [E1, E2] = xi::alg::Alg_Energy::surface_stretch_bending_energy(S);
        std::cout << "e1: " << E1 << " e2: " << E2 << std::endl;

        auto face = generate_trimmed_face(boundaries, S);
        faces.push_back(face);
    }

    m_shapes.clear();
    for (auto face : faces)
        add_shape(face);

    ui->occWidget->update_interactive_context();
}

void MainWindow::coverDBS()
{
    std::vector<gp_Pnt> points;
    std::vector<TopoDS_Shape> faces;
    for (auto sh : m_shapes)
    {
        TopExp_Explorer Ex;
        BRep_Tool tool;

        int edgeCount = 0;
        std::vector<std::shared_ptr<xi::geom::Geom_BSplineCurve<3>>> curves;
        for (Ex.Init(sh, TopAbs_EDGE); Ex.More(); Ex.Next())
        {
            TopoDS_Edge V;
            V = TopoDS::Edge(Ex.Current());

            double first, last;
            auto curve = Handle(Geom_BSplineCurve)::DownCast(tool.Curve(V, first, last));
            curves.push_back(Adaptor_OCCBSplineCurve<3>(curve));
            edgeCount++;
        }

        auto boundaries = curves;
        xi::alg::Alg_DBS_Approximator<3> approximator;
        approximator.wStretch = 1e-3;
        approximator.wBending = 1e-1;

        std::size_t samples = 100;
        for (std::size_t i = 0; i < curves.size(); i++)
        {
            auto curve = curves[i];
            auto first = curve->first_param();
            auto last = curve->last_param();
            auto step = (last - first) / (samples - 1);
            for (std::size_t j = 0; j < samples; j++)
            {
                auto param = first + j * step;
                auto point = curve->value(param);
                approximator.data.push_back(point);
                points.push_back(xi::utils::Adaptor_OCCVec(point));
            }
        }

        std::shared_ptr<xi::geom::Geom_BSplineSurface<3>> S;
        auto iterBase = approximator.generate();
        for (auto &sur : iterBase(3))
            S = sur;

        Handle(Geom_BSplineSurface) surface = xi::utils::Adaptor_OCCBSplineSurface(S.get());
        auto face = BRepBuilderAPI_MakeFace(surface, 1e-12);
        // auto face = generate_trimmed_face(boundaries, S);
        faces.push_back(face);

        std::cout << "distance: " << 0 << std::endl;
        auto [E1, E2] = xi::alg::Alg_Energy::surface_stretch_bending_energy(S);
        std::cout << "e1: " << E1 << " e2: " << E2 << std::endl;
    }

    m_shapes.clear();
    for (auto face : faces)
        add_shape(face);
    add_point_cloud(points);

    ui->occWidget->update_interactive_context();
}

void MainWindow::computeDistance()
{
    std::vector<double> firsts, seconds;
    std::vector<Handle(Geom_Curve)> rList;
    std::vector<Handle(Geom_Surface)> sList;
    for (auto shape : m_shapes)
    {
        TopExp_Explorer Ex;
        BRep_Tool tool;

        for (Ex.Init(shape, TopAbs_EDGE); Ex.More(); Ex.Next())
        {
            TopoDS_Edge E;
            E = TopoDS::Edge(Ex.Current());
            double first, second;
            auto edge = TopoDS::Edge(E);
            auto curve = BRep_Tool::Curve(edge, first, second);
            rList.push_back(curve);
            firsts.push_back(first);
            seconds.push_back(second);
        }

        for (Ex.Init(shape, TopAbs_FACE); Ex.More(); Ex.Next())
        {
            TopoDS_Face F;
            F = TopoDS::Face(Ex.Current());
            auto surface = BRep_Tool::Surface(F);
            sList.push_back(surface);
        }
    }

    auto surface = sList[0];
    double mse = 0;
    int n = 1000;
    double U1, U2, V1, V2;
    surface->Bounds(U1, U2, V1, V2);
    for (int i = 0; i < rList.size(); i++)
    {
        double first = firsts[i];
        double second = seconds[i];
        auto curve = rList[i];

        for (int j = 0; j < n; j++)
        {
            double u = first + (second - first) * j / (n - 1);
            auto P = curve->Value(u);

            GeomAPI_ProjectPointOnSurf Proj(P, surface, U1, U2, V1, V2);
            double d = Proj.LowerDistance();
            mse += d * d;
        }
    }

    qDebug() << "MSE: " << mse / (n * rList.size());
}

void MainWindow::computeEnergy()
{
    for (auto shape : m_shapes)
    {
        TopExp_Explorer Ex;
        BRep_Tool tool;

        for (Ex.Init(shape, TopAbs_FACE); Ex.More(); Ex.Next())
        {
            TopoDS_Face F;
            F = TopoDS::Face(Ex.Current());
            auto surface = Handle(Geom_BSplineSurface)::DownCast(BRep_Tool::Surface(F));
            if (surface.IsNull())
                continue;

            xi::utils::Adaptor_OCCBSplineSurface<3> adaptor(surface);
            std::shared_ptr<xi::geom::Geom_BSplineSurface<3>> geom_surface = adaptor;
            auto [E1, E2] = xi::alg::Alg_Energy::surface_stretch_bending_energy(geom_surface);
            std::cout << "e1: " << E1 << " e2: " << E2 << std::endl;
        }
    }
}

void MainWindow::rangeWeight()
{
    double wBending = 0.1;
    for (int i = 0; i < 8; i++)
    {
        std::vector<TopoDS_Shape> faces;
        for (auto sh : m_shapes)
        {
            TopExp_Explorer Ex;
            BRep_Tool tool;

            int edgeCount = 0;
            std::vector<std::shared_ptr<xi::geom::Geom_BSplineCurve<3>>> curves;
            for (Ex.Init(sh, TopAbs_EDGE); Ex.More(); Ex.Next())
            {
                TopoDS_Edge V;
                V = TopoDS::Edge(Ex.Current());

                double first, last;
                auto curve = Handle(Geom_BSplineCurve)::DownCast(tool.Curve(V, first, last));
                curves.push_back(Adaptor_OCCBSplineCurve<3>(curve));
                edgeCount++;
            }

            auto boundaries = curves;
            auto covering = xi::alg::Alg_FairingPIA_Covering{};
            covering.wBendingInterior = wBending;

            TIMER_START(Covering);
            auto S = covering.cover(std::move(boundaries));
            TIMER_END(Covering);

            std::cout << "distance: " << 0 << std::endl;
            auto [E1, E2] = xi::alg::Alg_Energy::surface_stretch_bending_energy(S);
            std::cout << "e1: " << E1 << " e2: " << E2 << std::endl;

            auto face = generate_trimmed_face(boundaries, S);
            faces.push_back(face);
        }

        auto name = TEST_DATA_PATH "/test" + std::to_string(i) + ".stp";
        ::write_stp(faces, name.c_str());
        wBending /= 10;
    }

    ui->occWidget->update_interactive_context();
}

void MainWindow::discrete()
{
    int uCount = 30;
    int vCount = 30;

    // alg::Alg_Sampler *sampler = nullptr;

    for (auto shape : m_shapes)
    {
        TopExp_Explorer Ex;
        BRep_Tool tool;

        std::vector<gp_Pnt> points;
        add_point_cloud(points);
        for (Ex.Init(shape, TopAbs_EDGE); Ex.More(); Ex.Next())
        {
            TopoDS_Edge E;
            E = TopoDS::Edge(Ex.Current());
            double first, second;
            auto edge = TopoDS::Edge(E);
            auto curve = BRep_Tool::Curve(edge, first, second);

            for (int i = 0; i < 1000; i++)
            {
                double u = first + (second - first) * i / 999;
                auto P = curve->Value(u);
                points.push_back(P);
            }
        }
        m_shapes.clear();
        add_point_cloud(points);
    }

    //     if (ui->uniformSample->isChecked())
    //         sampler = new sampling::UniformSampling(uCount, vCount);
    //     else if (ui->randSample->isChecked())
    //         sampler = new sampling::RandomSampling(uCount * vCount);
    //     else if (ui->poissonSample->isChecked())
    //         sampler = new sampling::PoissonSampling();
    //     else
    //         throw std::runtime_error("unsupported sampling method");

    //     TColgp_HSequenceOfPnt cloud;
    //     if (ui->curSel->isChecked())
    //     {
    //         for (int i = 1; i <= m_parent->m_selected.Size(); i++)
    //         {
    //             TColgp_HSequenceOfPnt points;
    //             auto shape = m_parent->m_selected.Value(i);
    //             if (shape.ShapeType() == TopAbs_EDGE)
    //                 points = (*sampler)(TopoDS::Edge(shape));
    //             else if (shape.ShapeType() == TopAbs_FACE)
    //                 points = (*sampler)(TopoDS::Face(shape));

    //             for (int j = 1; j <= points.Length(); j++)
    //                 cloud.Append(points.Value(j));
    //         }
    //     }
    //     else
    //     {
    //         // 初始化解释器
    //         pybind11::scoped_interpreter guard{};

    //         // 构造曲线公式
    //         auto sur = [&](double u, double v) {
    //             char code[255];
    //             sprintf_s(code, R"(
    // def sur(u, v):
    //     return u, v, %s

    // x, y, z = sur(%f, %f)
    // )",
    //                       ui->formula->text().toStdString().c_str(), u, v);

    //             pybind11::exec(code);
    //             return gp_Pnt(pybind11::globals()["x"].cast<double>(), pybind11::globals()["y"].cast<double>(),
    //                           pybind11::globals()["z"].cast<double>());
    //         };

    //         for (int i = 0; i < uCount; i++)
    //         {
    //             for (int j = 0; j < vCount; j++)
    //             {
    //                 double u = 1.0 * i / (uCount - 1);
    //                 double v = 1.0 * j / (vCount - 1);
    //                 cloud.Append(sur(u, v));
    //             }
    //         }
    //     }

    //     // 如果点列非空才创建点云
    //     if (!cloud.IsEmpty())
    //         m_parent->AddPointCloud(cloud);

    //     // 刷新交互
    //     m_parent->GetOCCWidget()->UpdateInteractiveContext();

    //     delete sampler;
}